package com.ruoyi.gateway.filter;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.symmetric.AES;
import com.ruoyi.common.core.constant.Constants;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * 在Controller执行之后处理响应数据的过滤器 该过滤器在请求处理完成后执行，可用于获取和处理响应体等操作
 *
 * @author shenli
 * @version v0.1.0
 * @since 2025/10/18 16:58
 */
@Component
@RefreshScope
public class EncryptFilter implements GlobalFilter, Ordered {

  @Value("${security.api.encrypt:false}")
  private Boolean apiEncrypt;

  private static final String HEADER_PRIVATE_WORD = "private-word";
  private static final String[] REQUEST_CONTENT_TYPE = {"multipart/form-data", "application/x-www-form-urlencoded"};

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

    System.out.println("运行 EncryptFilter " + exchange.getRequest().getURI());

    if (!apiEncrypt) {
      return chain.filter(exchange);
    }

    //shenli 25-10-24 17:49 特定请求类型的响应体不进行加密
    final MediaType requestContentType = exchange.getRequest().getHeaders().getContentType();
    final String requestContentTypeStr = requestContentType == null ? "" : requestContentType.getType() + "/" + requestContentType.getSubtype();
    if (Arrays.asList(REQUEST_CONTENT_TYPE).contains(requestContentTypeStr.toLowerCase())) {
      return chain.filter(exchange);
    }

    ServerHttpResponse serverHttpResponse = exchange.getResponse();
    DataBufferFactory bufferFactory = serverHttpResponse.bufferFactory();

    // 包装响应以捕获响应体
    ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(serverHttpResponse) {
      @Override
      public @NonNull Mono<Void> writeWith(@NonNull Publisher<? extends DataBuffer> body) {

        if (body instanceof Flux) {
          Flux<? extends DataBuffer> fluxBody = Flux.from(body);

          return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
            //shenli 25-10-18 14:06 把分段的响应体，合并为一个整体
            final byte[][] content = {new byte[0]};
            try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
              dataBuffers.forEach(dataBuffer -> {
                try {
                  // 读取响应体内容
                  byte[] part = new byte[dataBuffer.readableByteCount()];
                  dataBuffer.read(part);
                  byteArrayOutputStream.write(part, 0, part.length);
                } finally {
                  DataBufferUtils.release(dataBuffer);
                }
              });
              content[0] = byteArrayOutputStream.toByteArray();
            } catch (Exception e) {
              // 处理可能的IO异常
              throw new RuntimeException("Failed to read data buffers", e);
            }

            //shenli 25-10-18 14:08 生成AES密钥，并将密钥进行RSA加密后发到前端
            String aesKey = RandomUtil.randomString(16);
            final RSA rsa = new RSA(Constants.PRIVATE_KEY, null);
            final String keyBase64 = rsa.encryptBase64(aesKey, KeyType.PrivateKey);
            serverHttpResponse.getHeaders().add(HEADER_PRIVATE_WORD, keyBase64);

            //shenli 25-10-18 14:07 得到完整响应体的字符串，并用刚刚的密钥进行加密
            String bodyString = new String(content[0], StandardCharsets.UTF_8);
            final AES aes = new AES(Mode.ECB, Padding.PKCS5Padding, aesKey.getBytes());
            final String aesString = aes.encryptHex(bodyString);

            //shenli 25-10-18 19:20 尝试使用二进制传输响应体
//            System.out.println("content[0].length = " + content[0].length);
//            final byte[] cipherByte = AesUtils.aesCipherByte(content[0], aesKey, AesEnum.ENCRYPT);
//            System.out.println("aesString.length() = " + aesString.length());
//            System.out.println("cipherByte.length = " + cipherByte.length);

            // 重新包装数据缓冲区以供后续处理
            return bufferFactory.wrap(aesString.getBytes(StandardCharsets.UTF_8));
//            return bufferFactory.wrap(cipherByte);
//            return bufferFactory.wrap(content[0]);
          }));
        }
        return super.writeWith(body);
      }

    };

    // 使用装饰后的响应继续过滤器链
    return chain.filter(exchange.mutate().response(decoratedResponse).build());
  }

  @Override
  public int getOrder() {
    return -100;
  }

}
